package com.sdgs.paas.sdk.core.web;

import static com.sdgs.paas.sdk.core.PaasResponseEnum.IDEMPOTENCE_CHECK_FAIL;
import static com.sdgs.paas.sdk.core.PaasResponseEnum.IDEMPOTENCE_COMMIT_FAIL;
import static com.sdgs.paas.sdk.core.PaasResponseEnum.IDEMPOTENCE_COMMIT_SUCCESS;
import static com.sdgs.paas.sdk.core.PaasResponseEnum.IDEMPOTENCE_DEAL_TIMEOUT;
import static com.sdgs.paas.sdk.core.PaasResponseEnum.IDEMPOTENCE_DEAL_FAIL;
import static com.sdgs.paas.sdk.core.PaasResponseEnum.IDEMPOTENCE_CHECK_SUCCESS;

import java.util.List;
import java.util.concurrent.TimeUnit;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.HttpMethod;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

import com.codahale.metrics.Counter;
import com.codahale.metrics.MetricRegistry;
import com.codahale.metrics.Slf4jReporter;
import com.codahale.metrics.Timer;
import com.codahale.metrics.Timer.Context;
import com.sdgs.paas.sdk.core.PaasBaseException;
import com.sdgs.paas.sdk.core.PaasBaseResponse;
import com.sdgs.paas.sdk.core.PaasCacheDTO;
import com.sdgs.paas.sdk.core.log.PaasLogger;
import com.sdgs.paas.sdk.core.web.client.PaasRestTemplate;

/**
 * HTTP请求header处理拦截器
 *
 * @author wjj
 *
 */
public class PaasHeaderInterceptor extends HandlerInterceptorAdapter {

	@Autowired
	PaasHttpHeader paasHttpHeader;
	@Autowired
	private PaasRestTemplate restTemplate;
	@Autowired
	PaasHttpProperties paasHttpProperties;

	ThreadLocal<Boolean> checkIdempotence = new ThreadLocal<>();
	MetricRegistry registry;
	// Meter meterTps;
	Counter reqcnt;
	Timer reqtimer;
	final String metricSRC = MetricRegistry.name("shtelpaas", "request", "count");
	final String metricSRT = MetricRegistry.name("shtelpaas", "request", "timer");
	private final String IDEMPOTENCE_CHECK_KEY = "idempotence_check_key";
	private final String IDEMPOTENCE_CHECK_VAL = "idempotence_check_val";

	public PaasHeaderInterceptor(MetricRegistry registry) {
		super();
		this.registry = registry;
		Slf4jReporter slf4jReporter = Slf4jReporter.forRegistry(registry).build();
		slf4jReporter.start(5, TimeUnit.SECONDS);
		// meterTps =
		// registry.meter(MetricRegistry.name("shtelpaas","request","tps"));
		reqcnt = registry.counter(metricSRC);
		reqtimer = registry.timer(metricSRT);
	}

	@Override
	public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
			throws Exception {
		// 幂等性功能是否开启
		if (paasHttpProperties.isEnableIdempotence()) {
			// 本次请求是否执行幂等性
			boolean needCheckIdempotence = true;
			List<String> excludeIdempotence = paasHttpProperties.getExcludeIdempotence();
			if (excludeIdempotence != null && excludeIdempotence.contains(request.getRequestURI())) {
				needCheckIdempotence = false;
			}
			checkIdempotence.set(needCheckIdempotence);

			if (needCheckIdempotence) {
				// 发送请求给幂等性服务检查是否可以进行操作
				ParameterizedTypeReference<PaasBaseResponse<PaasCacheDTO>> responseType = new ParameterizedTypeReference<PaasBaseResponse<PaasCacheDTO>>() {
				};
				String url = paasHttpProperties.getIdempotenceCheckUrl();
				String reqbznum = request.getHeader(PaasHttpHeader.ReqBizNum);
				PaasBaseResponse<PaasCacheDTO> result = restTemplate
						.exchange(url, HttpMethod.GET, null, responseType, reqbznum).getBody();
				if (result.getCode().equals(IDEMPOTENCE_CHECK_SUCCESS.code())) {

					// 幂等性检查成功，保留数据
					request.setAttribute(IDEMPOTENCE_CHECK_KEY, result.getBody().getKey());
					request.setAttribute(IDEMPOTENCE_CHECK_VAL, result.getBody().getVal());

				} else {
					// 幂等性检查失败
					throw new PaasBaseException(IDEMPOTENCE_CHECK_FAIL.msg(), IDEMPOTENCE_CHECK_FAIL.code(),
							IDEMPOTENCE_CHECK_FAIL.msg());
				}
			}
		}
		Context ctx = reqtimer.time();
		request.setAttribute(metricSRT, ctx);

		paasHttpHeader.initHystrixRequestContext(request);
		// 响应头，要在没有生成响应体之前就设置好
		paasHttpHeader.setPaasRepHeader(response);
		return true;
	}

	@Override
	public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
			throws Exception {
		paasHttpHeader.shutdownHystrixRequestContext();

		registry.counter(metricSRC).inc();
		((Context) request.getAttribute(metricSRT)).stop();
		// 幂等性功能是否开启
		if (paasHttpProperties.isEnableIdempotence()) {
			// 本次请求是否执行幂等性
			Boolean needCheckIdempotence = checkIdempotence.get();
			if (needCheckIdempotence) {
				// 发送请求给幂等性服务持久化key或者删除key
				ParameterizedTypeReference<PaasBaseResponse<PaasCacheDTO>> responseType = new ParameterizedTypeReference<PaasBaseResponse<PaasCacheDTO>>() {
				};
				String url = paasHttpProperties.getIdempotenceCommitUrl();
				String key = (String) request.getAttribute(IDEMPOTENCE_CHECK_KEY);
				String opresult = ex == null ? "true" : "false";
				String cacheresult = (String) request.getAttribute(IDEMPOTENCE_CHECK_VAL);

				PaasBaseResponse<PaasCacheDTO> result = restTemplate
						.exchange(url, HttpMethod.POST, null, responseType, key, opresult, cacheresult).getBody();

				if (result.getCode().equals(IDEMPOTENCE_COMMIT_SUCCESS.code())) {
					PaasLogger.info("业务已处理，幂等性持久化成功，key={}", key);
				} else if (result.getCode().equals(IDEMPOTENCE_DEAL_TIMEOUT.code())) {
					if (opresult.equals("true")) {
						PaasLogger.info("业务已处理，幂等性持久化前业务已超时，key={}，需要人工干预，监控请告警", key);
					} else {
						PaasLogger.info("业务处理失败，幂等性持久化前业务已超时，key={}", key);
					}

				} else if (result.getCode().equals(IDEMPOTENCE_COMMIT_FAIL.code())) {
					PaasLogger.error("业务已处理，幂等性持久化失败，key={}，需要人工干预，监控请告警", key);
				} else if (result.getCode().equals(IDEMPOTENCE_DEAL_FAIL.code())) {
					PaasLogger.info("业务处理失败，幂等性key={} 已被删除", key);
				}

			}
		}

	}

	// public void postHandle(HttpServletRequest request, HttpServletResponse
	// response, Object handler,
	// ModelAndView modelAndView) throws Exception {
	//
	// shutdownHystrixRequestContext();
	// long st=(long) request.getAttribute("shtelpaas.request.starttime");
	//
	// registry.getCounters().get("shtelpaas.request.count").inc();
	// registry.getCounters().get("shtelpaas.request.totaltime").inc(System.currentTimeMillis()-st);
	// }

}
